
package com.huaweicloud.saaslanding.install.bootstrap;

import com.huaweicloud.saaslanding.basic.constant.KafkaConstant;
import com.huaweicloud.saaslanding.basic.enumeration.DatabaseTypeEnum;
import com.huaweicloud.saaslanding.basic.exception.BizException;
import com.huaweicloud.saaslanding.basic.exception.code.ExceptionCode;
import com.huaweicloud.saaslanding.install.SaaslandingInstallApplication;
import com.huaweicloud.saaslanding.install.applicationservice.CceService;
import com.huaweicloud.saaslanding.install.applicationservice.CdnService;
import com.huaweicloud.saaslanding.install.applicationservice.DnsService;
import com.huaweicloud.saaslanding.install.applicationservice.ElbService;
import com.huaweicloud.saaslanding.install.applicationservice.IdaasService;
import com.huaweicloud.saaslanding.install.applicationservice.KafkaService;
import com.huaweicloud.saaslanding.install.applicationservice.LtsService;
import com.huaweicloud.saaslanding.install.applicationservice.ObsService;
import com.huaweicloud.saaslanding.install.applicationservice.RdsService;
import com.huaweicloud.saaslanding.install.applicationservice.RedisService;
import com.huaweicloud.saaslanding.install.applicationservice.RocketMQService;
import com.huaweicloud.saaslanding.install.applicationservice.ServiceManageFrameService;
import com.huaweicloud.saaslanding.install.applicationservice.SmnService;
import com.huaweicloud.saaslanding.install.applicationservice.VpcService;
import com.huaweicloud.saaslanding.install.applicationservice.WafService;
import com.huaweicloud.saaslanding.install.enumeration.SetupEnum;
import com.huaweicloud.saaslanding.install.properties.DatabaseConfig;
import com.huaweicloud.saaslanding.install.properties.Idaas;
import com.huaweicloud.saaslanding.install.properties.Lts;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Stack;
import java.util.function.Consumer;

/**
 * @since 2022-03-25
 */

@Component
@Slf4j
public class SetupProcess implements ISetupProcess {
    private final Stack<Consumer<SetupEnum>> initConsumerStack = new Stack<>();

    private final Stack<Consumer<SetupEnum>> cleanConsumerStack = new Stack<>();

    // 标志是否需要加载清除操作，仅每次启动直接清除时才加载一次
    private Boolean isLoadNeeded = true;

    // 标志是否需要提示跳过当前清除步骤，当clean不完全时提示是否跳过当前失败的步骤
    private Boolean isPromptNeeded = true;

    @Resource
    private ServiceManageFrameService serviceManageFrameService;

    @Autowired(required = false)
    private RocketMQService rocketMQService;

    @Resource
    private DatabaseConfig databaseConfig;

    @Resource
    private IdaasService idaasService;

    @Autowired(required = false)
    private KafkaService kafkaService;

    @Resource
    private RedisService redisService;

    @Resource
    private CceService cceService;

    @Resource
    private ElbService elbService;

    @Autowired(required = false)
    private LtsService ltsService;

    @Resource
    private ObsService obsService;

    @Resource
    private RdsService rdsService;

    @Resource
    private SmnService smnService;

    @Resource
    private VpcService vpcService;

    @Resource
    private WafService wafService;

    @Resource
    private CdnService cdnService;

    @Resource
    private DnsService dnsService;

    @Resource
    private Idaas idaas;

    @Resource
    private Lts lts;

    /**
     * 执行操作
     *
     * @param command 命令：SetupEnum的INSTALL或CLEAN
     */
    @Override
    public void execute(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            if (initConsumerStack.isEmpty()) {
                generateInitConsumer();
            }
            isLoadNeeded = false;
            isPromptNeeded = false;

            while (!initConsumerStack.isEmpty()) {
                nextInit();
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            if (isLoadNeeded) {
                // 启动后直接清除，不需要提示
                generateCleanConsumer();
                isPromptNeeded = false;
            } else {
                // 如果初始化时出错，清除之前需要清空初始化操作
                // 保证清除之后再初始化不会遗漏已经执行的步骤
                initConsumerStack.clear();
            }

            if (isPromptNeeded) {
                log.info("\n------检测到之前clean时出错，\n------是否跳过之前出错的步骤执行下一步?\n------(y(Y) for yes, others for no)");
                String input = SaaslandingInstallApplication.scanner.nextLine();
                // 出错时cleanStack一定不为空
                if ("y".equalsIgnoreCase(input)) {
                    cleanConsumerStack.pop();
                }
            } else if (!rdsService.canInitOrRollbackCceAndCse()) {
                return;
            }

            // 清除前认为会出错，全部清除时为未出错
            isPromptNeeded = true;
            while (!cleanConsumerStack.isEmpty()) {
                nextClean();
            }
            isPromptNeeded = false;
        } else {
            log.info("不支持的命令，请重新输入");
        }
    }

    /**
     * 执行rds相关操作
     *
     * @param command 命令
     */
    private void rds(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            rdsService.initDataBase();
            // 不能初始化时，将接下来的初始化操作清空
            if (!rdsService.canInitOrRollbackCceAndCse()) {
                initConsumerStack.clear();
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            rdsService.rollbackDataBase();
        }
    }

    /**
     * 执行redis相关操作
     *
     * @param command 命令
     */
    private void redis(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            log.info("初始化时不需要单独设置redis数据");
        } else if (SetupEnum.CLEAN.equals(command)) {
            redisService.deleteRedis();
        }
    }

    /**
     * 执行idaas相关操作
     *
     * @param command 命令
     */
    private void idaas(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            idaasService.initIdaasInfo();
        } else if (SetupEnum.CLEAN.equals(command)) {
            idaasService.deleteIdaasInfo();
        }
    }

    /**
     * 执行注册中心相关操作
     *
     * @param command 命令
     */
    private void serviceManager(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            serviceManageFrameService.initServiceManageFramework();
        } else if (SetupEnum.CLEAN.equals(command)) {
            serviceManageFrameService.deleteAllConfig();
        }
    }

    /**
     * 执行cce相关操作
     *
     * @param command 命令
     */
    private void cce(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            cceService.initCce();
        } else if (SetupEnum.CLEAN.equals(command)) {
            cceService.deleteNamespace();
        }
    }

    /**
     * 执行日志资源相关操作
     *
     * @param command 命令
     */
    private void logResource(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            ltsService.tryInitOpLogResource();
            ltsService.tryInitSysLogResource();
        } else if (SetupEnum.CLEAN.equals(command)) {
            ltsService.tryDeleteLogResource();
        }
    }

    /**
     * 执行日志消费组相关操作
     *
     * @param command 命令
     */
    private void logConsumer(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            if (KafkaConstant.ROCKETMQ_CONSUMER.equalsIgnoreCase(lts.getConsumer())) {
                rocketMQService.initRocketMQ();
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            if (KafkaConstant.KAFKA_CONSUMER.equalsIgnoreCase(lts.getConsumer())) {
                // 删除Kafka主题及消费者
                kafkaService.deleteKafka();
            } else if (KafkaConstant.ROCKETMQ_CONSUMER.equalsIgnoreCase(lts.getConsumer())) {
                // 删除RocketMQ主题及消费者
                rocketMQService.deleteRocketMQ();
            } else {
                log.warn("启用了LTS，但消息中间件类型设置错误，当前只支持Kafka和RocketMQ，设置的类型为：<{}>", lts.getConsumer());
            }
        }
    }

    /**
     * 执行AOM接入规则相关操作
     *
     * @param command 命令
     */
    private void mappingRule(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            ltsService.initMappingRule();
        } else if (SetupEnum.CLEAN.equals(command)) {
            ltsService.deleteMappingRule();
        }
    }

    /**
     * 执行obs相关操作
     *
     * @param command 命令
     */
    private void obs(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            obsService.initObs();
        } else if (SetupEnum.CLEAN.equals(command)) {
            obsService.deleteObs();
        }
    }

    /**
     * 执行logo相关操作
     *
     * @param command 命令
     */
    private void logo(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            obsService.initLogo();
        } else if (SetupEnum.CLEAN.equals(command)) {
            obsService.deleteLogo();
        }
    }

    /**
     * 执行elb相关操作
     *
     * @param command 命令
     */
    private void elb(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            elbService.initElb();
        } else if (SetupEnum.CLEAN.equals(command)) {
            elbService.deleteElb();
        }
    }

    /**
     * 执行SMN资源相关操作
     *
     * @param command 命令
     */
    private void smnResource(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            log.info("初始化时不需要单独设置smn资源");
        } else if (SetupEnum.CLEAN.equals(command)) {
            smnService.clearCurrentEnvironment();
        }
    }

    /**
     * 执行消息模版相关操作
     *
     * @param command 命令
     */
    private void messageTemplate(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            smnService.createDefaultMessageTemplate();
        } else if (SetupEnum.CLEAN.equals(command)) {
            smnService.deleteDefaultMessageTemplate();
        }
    }

    /**
     * 执行vpc相关操作
     *
     * @param command 命令
     */
    private void vpc(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            vpcService.initVpcPeering();
        } else if (SetupEnum.CLEAN.equals(command)) {
            vpcService.deleteVpcPeering();
        }
    }

    /**
     * 执行工单相关操作
     *
     * @param command 命令
     */
    private void worksheet(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            rdsService.initWorksheet();
        } else if (SetupEnum.CLEAN.equals(command)) {
            obsService.deleteWorksheet();
        }
    }

    /**
     * 执行数据库连接信息相关操作
     *
     * @param command 命令
     */
    private void datasource(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            // 根据数据库类型，初始化gaussdb_instance_info或rds_instance_info表
            if (DatabaseTypeEnum.RDS.getType().equals(databaseConfig.getType())) {
                rdsService.initRdsInstance();
            } else if (DatabaseTypeEnum.GAUSS_DB.getType().equals(databaseConfig.getType())) {
                rdsService.initGaussDBInstance();
            } else {
                log.warn("暂不支持的数据库类型: <{}>，无法购买尊享版套餐及查看资源监控中的数据库信息...", databaseConfig.getType());
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            log.info("清除时不需要单独删除数据库类型信息");
        }
    }

    /**
     * 执行waf相关操作
     *
     * @param command 命令
     */
    private void waf(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            // 初始化WAF证书到OBS桶中
            Long tenantCertificateId = wafService.createCertificate();
            // 创建WAF防护域名
            wafService.createCloudProtectionDomain(tenantCertificateId);
        } else if (SetupEnum.CLEAN.equals(command)) {
            wafService.deleteDomain();
        }
    }

    /**
     * 执行cdn相关操作
     *
     * @param command 命令
     */
    private void cdn(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            try {
                cdnService.createAndConfigDomain();
            } catch (Exception e) {
                log.error("初始化CDN加速域名失败：{}", e.toString());
                throw new BizException(ExceptionCode.INITIALIZE_CDN_FAILED);
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            cdnService.deleteDomain();
        }
    }

    /**
     * 执行dns相关操作
     *
     * @param command 命令
     */
    private void dns(SetupEnum command) {
        if (SetupEnum.INSTALL.equals(command)) {
            try {
                dnsService.createRecordSet();
            } catch (Exception e) {
                log.error("初始化DNS记录集失败：{}", e.toString());
                throw new BizException(ExceptionCode.INITIALIZE_DNS_FAILED);
            }
        } else if (SetupEnum.CLEAN.equals(command)) {
            dnsService.deleteRecordSet();
        }
    }

    /**
     * 初始化的顺序，先执行的后push
     */
    @PostConstruct
    private void generateInitConsumer() {

        initConsumerStack.push(this::dns);
        initConsumerStack.push(this::cdn);
        initConsumerStack.push(this::waf);

        if (idaas.getApplicationHttpsMonoLicense().getEnabled()) {
            initConsumerStack.push(this::vpc);
        }

        initConsumerStack.push(this::messageTemplate);
        initConsumerStack.push(this::smnResource);
        initConsumerStack.push(this::elb);
        initConsumerStack.push(this::worksheet);
        initConsumerStack.push(this::logo);
        initConsumerStack.push(this::obs);

        if (lts.isEnabled()) {
            initConsumerStack.push(this::mappingRule);
            initConsumerStack.push(this::logResource);
        }

        initConsumerStack.push(this::cce);

        if (lts.isEnabled()) {
            initConsumerStack.push(this::logConsumer);
        }

        initConsumerStack.push(this::serviceManager);
        initConsumerStack.push(this::idaas);
        initConsumerStack.push(this::datasource);
        initConsumerStack.push(this::redis);
        initConsumerStack.push(this::rds);

        cleanConsumerStack.clear();
    }

    /**
     * 清除的顺序，与初始化顺序相反<br>
     * 如有初始化和清除需要保持顺序的操作，即先初始化也先清除<br>
     * 可以在while中手动指定顺序
     */
    private void generateCleanConsumer() {
        while (!initConsumerStack.isEmpty()) {
            cleanConsumerStack.push(initConsumerStack.pop());
        }

        isLoadNeeded = false;
    }

    /**
     * 执行下一步init
     */
    private void nextInit() {
        // 先pop，再accept；
        // 不需要清除失败的初始化操作时，改为先accept，再pop
        cleanConsumerStack.push(initConsumerStack.pop());
        cleanConsumerStack.peek().accept(SetupEnum.INSTALL);
    }

    /**
     * 执行下一步clean
     */
    private void nextClean() {
        // 先accept，再pop
        cleanConsumerStack.peek().accept(SetupEnum.CLEAN);
        cleanConsumerStack.pop();
    }

}
